#!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec tclsh "$0" ${1+"$@"}

# Author: jiyin@redhat.com
# Like but better than "bkr workflow-xxx --dryrun", more functional and flexible
# gen_job_xml --help for detail

lappend ::auto_path $::env(HOME)/lib /usr/local/lib /usr/lib64 /usr/lib
package require yaml
package require json
package require xmlgen
package require getOpt 3.0
package require runtestlib 1.1
namespace import ::xmlgen::* ::getOpt::* ::runtestlib::*

declaretags job whiteboard notify cc
declaretags recipeSet recipe and or not
declaretags distroRequires distro_name distro_family distro_tag distro_variant distro_method distro_arch
declaretags hostRequires hostname arch system_type key_value
declaretags repos repo
declaretags partitions partition
declaretags ks_appends ks_append
declaretags watchdog
declaretags task params param

if {[info commands lmap] == ""} {
	proc lmap args {
		set body [lindex $args end]
		set args [lrange $args 0 end-1]
		set n 0
		set pairs [list]
		foreach {varnames listval} $args {
			set varlist [list]
			foreach varname $varnames {
				upvar 1 $varname var$n
				lappend varlist var$n
				incr n
			}
			lappend pairs $varlist $listval
		}
		set temp [list]
		foreach {*}$pairs {
			lappend temp [uplevel 1 $body]
		}
		set temp
	}
}

# global var
array set Opt {}
array set InvalidOpt {}
set NotOptions [list]
set OptionList {
  "  Options:" {
	{help h}      {arg n	help {Print this usage}}
	F             {arg m	help {Specify a test list file}}
	opts-macro    {arg y	help {exec the "cmd [arg]" in run time to generate options} hide y}
  }

  "\n  Options for job configuration:" {
	harness			{arg y	help {specify alternative harness, available value: beah|restraint; default: restraint}}
	restraint-git		{arg y	help {specify restraint git repo}}
	{restraint-case-trim/ p}	{arg y  help {specify how many level should be removed from beaker task name}}
	restraint		{arg o	help {deprecated} hide y}
	cc			{arg m	help {Notify additional e-mail address on job completion}}
	job-owner		{arg y	help {Submit job on behalf of USERNAME (submitting user must be a submission delegate for job owner)}}
	{wb whiteboard}		{arg y	help {Set the whiteboard for this job}}
	repo			{arg m	help {Configure repo at <URL> in the kickstart for installation}}
	repo-post		{arg m	help {Configure repo at <URL> as part of kickstart %post execution}}
	recipe			{arg n	help {Just generate recipeSet node, internal use for runtest -merge}}
	rwb			{arg y	help {Set the whiteboard for the recipe}}
	{info flag}             {arg y	help {do nothinig, just used by caller} hide yes}
	retention-tag           {arg y  help {e.g: scratch, 60days, 120days, active, active+1, audit}}
	product                 {arg y  help {e.g: cpe:/o:redhat:enterprise_linux:$x:$y}}
	packages                {arg y	help {e.g: --packages=gcc,screen,wget. or use '/' instead ','}}
  }

  "\n  Options for selecting distro tree(s):" {
	{dr distrorequire}	{arg m	help {distrorequire --dr=key="value"}}
	{DR}			{arg m	help {distroRequires for multi-host test. e.g:
				--DR="distro_arch=x86_64"
				}}
	family			{arg m	help {Use latest distro of this FAMILY for job, eg. "RedHatEnterpriseLinux6", same as --dr=distro_family=\$FAMILY}}
	tag			{arg m	help {Use latest distro tagged with TAG, eg. "RTT_ACCEPTED" (default: STABLE), same as --dr=distro_tag=\$TAG}}
	distro			{arg y	help {Use named distro for job, same as --dr=distro_name=\$DISTRO}}
	distrot			{arg y	help {define distro template for multihost test, eg: --distrot=6,6,%D)}}
	variant			{arg m	help {Specify the distro variant, same as --dr=distro_variant=\$VARIANT}}
	arch			{arg y	help {Specify the distroRequire.disro_arch and hostRequire.arch, same as: --dr=distro_arch=\$ARCH --hr=arch=\$ARCH}}
  }

  "\n  Options for selecting system(s):" {
	{topo}			{arg y	help {Include NUMBER server and client hosts for multi-host test. eg --topo=multiHost.1.1}}
	{servers s}		{arg y	help {Include NUMBER server hosts for multi-host test}}
	{clients c}		{arg y	help {Include NUMBER client hosts for multi-host test}}
	{hr hostrequire}	{arg m	help {Additional <hostRequires><and/> for job, example:
				--hr cpu_count>=4
				--hr memory>=4096
				--hr labcontroller=lab-01.rhts.eng.pek2.redhat.com
				--hr device:type=STORAGE,driver=nvme
				--hr disk.phys_sector_size=4096,unit=bytes
				--hr key_value:key=CPUVENDOR,op==,value=\$Vendor  #same as --kv CPUVENDOR=\$Vendor
				--hr 'kv-DISKSPACE>=60000'      #same as --kv 'DISKSPACE>=60000'
				--hr kv-CPUVENDOR~%AMD%         #~%AMD%  =AuthenticAMD
				--hr 'system.numanodes>=4'      #same as --sr 'numanodes>=4'
				--hr top:system_type=Machine    #same as --systype=Machine
				--hr or:tag=value
				--hr not:kv-CPUVENDOR~%Intel%   #~%Intel%  =GenuineIntel
				}}
	{HR}			{arg m	help {hostRequires for multi-host test. e.g:
				--HR= --HR="memory>4096 key_value:key=DISK,op=>,value=80000"
				#means no requires for 1st host, and 2nd need more than 4096M ram and more than 80G disk size
				}}
	p9			{arg n  hide y}
	{kv keyvalue}		{arg m	help {Require system with matching legacy key-value, same as '--hr=key_value:key=K,op=Op,value=Val' example: --kv NETWORK=e1000}}
	{sr sysrequire}		{arg m	help {Additional <hostRequires><and><system/> for job, same as '--hr=system.key=value' example: --sr numanodes=4}}
	{opt-alias hr-alias}		{arg y	help {alias of long and changeable options combination}}
	{opt-alias-file hr-alias-file}		{arg y	help {opt-alias will search alias in this file}}
	machine			{arg m	help {Require the machine for job, set comma-separated values for multi-host, example: --machine=SERVER1,CLIENT1}}
	systype			{arg m	help {Require system of TYPE for job (Machine, Prototype, Laptop, ..) default: Machine}}
	ormachine		{arg m	help {Use comma-separated values to set a machine pool, example: --ormachine=HOST1,HOST2,HOST3}}
	random                  {arg n  help {autopick type}}
	hrxml                   {arg y  help {deprecated, see --hr option}}
  }

  "\n  Options for selecting special system(s) of networ-qe:" {
	nay-driver		{link netqe-nic-driver	hide y}
	nic-num			{link netqe-nic-num	hide y}
	nay-nic-driver		{link netqe-nic-driver	hide y}
	nay-nic-num		{link netqe-nic-num	hide y}
	nay-nic-model		{link netqe-nic-model	hide y}
	nay-nic-speed		{link netqe-nic-speed	hide y}
	nay-nic-match		{link netqe-nic-match	hide y}
	nay-nic-unmatch		{link netqe-nic-unmatch hide y}
	netqe-nic-driver	{arg o help ""}
	netqe-nic-num		{arg o help ""}
	netqe-nic-model		{arg o help ""}
	netqe-nic-speed		{arg o help ""}
	netqe-nic-match		{arg o help ""}
	netqe-nic-unmatch	{arg o help {These options together generate the machine pool which match required NIC num/driver/model/speed
				- Refer `parse_netqe_nic_info.sh -h`, which is the engine for the translating.
				  Example: --netqe-nic-driver=e1000e --netqe-nic-num=2
				- Use comma-separated values for different machine pool of multihost
				  Example: --netqe-nic-driver=e1000e,any --netqe-nic-num=2 --netqe-nic-speed=1g }}
  }

  "\n  Options for setting tasks:" {
	task			{arg m	help {Include named task in job, can use multiple times}}
	insert-task		{arg m	help {Insert named task before task list, can use multiple times}}
	append-task		{arg m	help {Append named task after task list, can use multiple times}}
	taskn			{arg y	help {repeat task 'N' times}}
	reboot			{arg y	help {add reboot after each task}}
	{param taskparam}	{arg m	help {Set task params, can use multiple times.
				Use "mh-" prefix to set different value for multihost, example: --param=mh-key=val1,val2}}
	noavc			{arg n	help {alias of --param=AVC_ERROR=+no_avc_check}}
	nvr			{arg m	help {Specify the kernel(Name-Version-Release) to be installed}}
	install			{arg m	help {Install PACKAGE using /distribution/pkginstall, can use multiple times}}
	upstream		{arg o	help {Specify the kernel src git addr to be installed. --upstream=[git://a.b.c/d[#branch[ tag]]]
				e.g: --upstream
				e.g: --upstream=#v4.18-rc1
				e.g: --upstream=git://git.linux-nfs.org/projects/bfields/linux.git#jbf-test-1}}
	upstream-use-clone	{arg o	help {use git-clone instead default git-archive, in case some repo doesn't support git-archive
				- the optional option param is additional option/param of git-clone
				e.g: --upstream-use-clone=--depth=1}}
	upstream-patch		{arg y	help {apply specified patch[es] before compile upstream kernel}}
	upstream-kernel-kasan   {arg n  help {Flag to enable upstream kernel kasan support}}
	Scratch			{arg m  help {Install scratch built package using /distribution/scratchinstall, can use multiple times}}
	scratch			{arg m  help {same as Scratch, but every specified just apply one host, if in multihost mode}}
	dbgk			{arg n	help {Use the debug kernel}}
	gcov			{arg y	help {Enable gcov for coverage data collection, use arg to specify Package, example: --gcov="nfs-utils"}}
	kcov			{arg o	help {Enable kcov for coverage data collection, use arg to specify KDIR, example: --kcov="fs, drivers/net"}}
	kdump			{arg o	help {Enable kdump using /kernel/kdump/setup-nfsdump}}
	nokdump			{arg n	help {disable kdump, some tests can not work with kdump}}
	cmd			{arg m	help {Add /distribution/command before test task}}
	cmdb			{arg m	help {Add /distribution/command before install kernel}}
	cmd-end			{arg m	help {Add /distribution/command in the end of the recipe}}
	leap-second		{arg n	help {Add leap-second task}}
	reserve-if-fail		{arg o	help {Reserve the machine if test fail, specify RESERVETIME with s/m/h/d unit, max amount is 99h}}
	reserve			{arg o	help {Reserve system at the end of the recipe}}
	fips                    {arg o  help {enable fips}}
	abrt                    {arg n  help {enable abrt(insert /distribution/crashes/enable-abrt)}}
	ignore-panic            {arg n  help {Do not abort job if panic message appears on serial console}}
  }

  "\n  Options for installation:" {
	{part partition}	{arg m	help {Additional <partitions/> for job, example: --part='fs=xfs name=/mnt/xfs size=10 type=part'}}
	method			{arg y	help {Installation source method (nfs, http, ftp)}}
	ks-meta			{arg m  help {Pass kickstart metadata OPTIONS when generating kickstart}}
	ks-append		{arg m	help {Specify additional kickstart commands to add to the base kickstart file}}
	ks			{link ks-append hide y}
	ksf			{arg o	help {Similar to --ks-append, but pass the content of a specified file}}
	{k-opts kernel-options}	{arg m	help {Pass OPTIONS to kernel during installation}}
	{k-opts-post kernel-options-post}	{arg m	help {Pass OPTIONS to kernel after installation}}
  }
}

proc Usage {} {
	puts "Usage: $::argv0 --distro=<DISTRO> \[options\]"
	puts "Generates a Beaker job XML file, like `bkr workflow-simple`, but have many improvements and unofficial options"
	puts "Example:  gen_job_xml --distro RHEL-6.6 --task=/distribution/reservesys --arch=x86_64"
	puts "Example:  gen_job_xml --family RedHatEnterpriseLinux7 -F test.list --nvr=kernel-x.y.z-abc.el7 --dbgk\n"
	getUsage $::OptionList
}

proc getDefaultNVR {_distro} {
	set reg $_distro
	if [regexp {^RHEL-[0-9]\.[0-9]$} $reg] {
		regsub {RHEL-([0-9]+\.[0-9]+)} $reg {RHEL-./\1} reg
	}

	set _NVR [exec vershow {^kernel-[2-6]+[-.0-9]+\.} $reg]
	if [regsub {.*(kernel-[2-6]+[-.0-9]+\.[^.]+).*} $_NVR {\1} _defaultNVR] {
		return $_defaultNVR
	} else {
		return {}
	}
}

# _parse_ argument
set option-alias-path http://pkgs.devel.redhat.com/cgit/tests/kernel/plain/filesystems/Inventory/hostrequire-alias.tcl
set bkrurl https://beaker.engineering.redhat.com
set aliasf alias.tcl
set ARGV $::argv
getOptions $OptionList $ARGV Opt InvalidOpt NotOptions
if {[info exist Opt(opt-alias)] && $Opt(opt-alias) != ""} {
	set alias $Opt(opt-alias)
	if {[info exist Opt(opt-alias-file)] && $Opt(opt-alias-file) != ""} {
		set option-alias-path $Opt(opt-alias-file)
	}
	if [file isfile ${option-alias-path}] {
		set aliasf ${option-alias-path}
	} else {
		exec bash -c "wget -q ${option-alias-path} -O ${aliasf}"
	}
	source ${aliasf}
	set idx [lindex [lsearch -all -regexp $ARGV {-(opt|hr)-alias(=.*)?$}] end]
	if [regexp -- {.*-(opt|hr)-alias$} [lindex $ARGV $idx]] {set ARGV [lreplace $ARGV $idx+1 $idx+1]}
	set ARGV [lreplace $ARGV $idx $idx]
	set NARGV [linsert $ARGV $idx {*}$Alias($alias)]
	getOptions $OptionList $NARGV Opt InvalidOpt NotOptions

	if ![file isfile ${option-alias-path}] {
		exec rm ${aliasf}
	}
}

proc debug {} {
	puts "NotOptions: $NotOptions"
	parray InvalidOpt
	parray Opt
}

if [info exist Opt(help)] {
	Usage
	exit 0
}

# process --param= option
set GlobalParam {}
if [info exist Opt(param)] { lappend GlobalParam {*}$Opt(param) }
if [info exist Opt(noavc)] { lappend GlobalParam "AVC_ERROR=+no_avc_check" }

# process --distro= option
set DISTRO_L {}
set FAMILY_L {}
set TAG_L {}
set _ldistro {}
set _lfamily {}
set _ltag {STABLE}
if [info exist Opt(distro)] {
	if [info exist Opt(distrot)] {
		set Opt(distro) [string map "%D $Opt(distro)" [expandDistro $Opt(distrot)]]
	}
	foreach e $Opt(distro) {lappend _ldistro {*}[split $e ", "]}
	set prev {}
	foreach d $_ldistro {
		if {[string length $d] > 0} {
			lappend DISTRO_L $d
			set prev $d
		} elseif {[string length $prev] > 0} {
			lappend DISTRO_L $prev
		}
	}
	lappend GlobalParam "DISTRO_BUILD=$DISTRO_L"
}
if [info exist Opt(family)] {
	foreach e $Opt(family) {lappend _lfamily {*}[split $e ", "]}
	set prev {}
	foreach family $_lfamily {
		if {[string length $family] > 0} {
			lappend FAMILY_L $family
			set prev $family
		} elseif {[string length $prev] > 0} {
			lappend FAMILY_L $prev
		}
	}
}
if [info exist Opt(tag)] {
	foreach e $Opt(tag) {lappend _ltag {*}[split $e ", "]}
	set prev {}
	foreach t $_ltag {
		if {[string length $t] > 0} {
			lappend TAG_L $t
			set prev $t
		} elseif {[string length $prev] > 0} {
			lappend TAG_L $prev
		}
	}
}

if [info exist Opt(upstream)] {
	set upstreamUrl git://git.app.eng.bos.redhat.com/linux.git
	set upstreamBranch master
	set upstreamTag HEAD
	if {$Opt(upstream) != ""} {
		lassign [split $Opt(upstream)] url_branch tag
		lassign [split $url_branch "#"] url branch
		if {[string trim $url] != ""} {set upstreamUrl $url}
		if {[string trim $branch] != ""} {set upstreamBranch $branch}
		if {[string trim $tag] != ""} {set upstreamTag $tag}
	}
}

if {[llength $DISTRO_L] == 0 && [llength $FAMILY_L] == 0} {
	puts stderr "Warning: no distro specified, use --distro= or --family= option"
	Usage
	exit 1
}

if {[regexp -- {RHEL-8} $DISTRO_L] == 1 && [info exist Opt(harness)]} {
	set Opt(harness) rst
	if [info exist Opt(wb)] {
		regsub -line {harness=[[:alnum:]]*} $Opt(wb) {harness=rst} Opt(wb)
	}
}

# process -F or --task= option
set TaskList {}
set TestList {}
if [info exist Opt(task)] { lappend TestList {*}$Opt(task) }
if [info exist Opt(F)] {
	foreach testfile $Opt(F) {
		set fp stdin
		if {$testfile ni "-"} {
			set fp [open $testfile]
		}

		while {-1 != [gets $fp line]} {
			lappend TestList $line
		}

		if {$testfile ni "-"} { close $fp }
	}
}
if {[llength $TestList] == 0} {
	puts stderr "Warning: no task specified, use -F or --task= option"
	Usage
	exit 1
}
foreach T $TestList {
	set test [string trim $T]
	if {[regexp -- {^#} $test] == 1} continue
	if {$test == "" || $test == "./"} continue
	regsub -line {^-  *} $test {} test

	set tdict [::yaml::yaml2dict $T]
	set tname [lindex $tdict 0]
	set tdict [lindex $tdict 1]

	if [dict exist $tdict param] {
		lappend TaskList [concat $tname [dict get $tdict param] $GlobalParam]
	} else {
		lappend TaskList [concat $tname $GlobalParam]
	}
}

set METHOD "nfs"
if [info exist Opt(method)] {
	set METHOD $Opt(method)
}

set SystemTypeList {}
if [info exist Opt(systype)] {
	foreach e $Opt(systype) {lappend SystemTypeList {*}[split $e ", "]}
}

set VARIANT_L {}
if [info exist Opt(variant)] {
	foreach e $Opt(variant) {lappend VARIANT_L {*}[split $e ", "]}
}

set KVARIANT "up"
if [info exist Opt(dbgk)] { set KVARIANT "debug" }

set MACHINE_L {}
if [info exist Opt(machine)] {
	foreach e $Opt(machine) {lappend MACHINE_L {*}[split $e ", "]}
}

set NVR_L {}
if [info exist Opt(nvr)] {
	foreach e $Opt(nvr) {lappend NVR_L {*}[split $e ", "]}
	if {[llength $DISTRO_L] == 1 && [llength $NVR_L] == 1} {
		set _defaultNVR [getDefaultNVR $DISTRO_L]
		if {$_defaultNVR == $NVR_L} {
			set NVR_L {}
		}
	}
}

set ORMACHINE_L {}
if [info exist Opt(ormachine)] {
	foreach e $Opt(ormachine) {lappend ORMACHINE_L {*}[split $e ", "]}
}

set ARCH_L {auto}
if [info exist Opt(arch)] {
	set ARCH_L {}
	foreach e $Opt(arch) {lappend ARCH_L {*}[split $e ", "]}
}

set FIPS_L {}
if [info exist Opt(fips)] {
	foreach e $Opt(fips) {lappend FIPS_L {*}[split $e ", "]}
}

set recipe_ks_meta {}
set harness restraint
if [info exist Opt(harness)] { set harness $Opt(harness) }
if [info exist Opt(restraint-git)] { set harness restraint }

if {$harness in {r re res rest restr restra restrai restrain restraint rst}} {
	set recipe_ks_meta "harness='restraint-rhts beakerlib'"
	if {[info exist Opt(restraint-git)] && $Opt(restraint-git) != ""} {
		set fetch_test_from_git yes
		set restraint_git $Opt(restraint-git)
	}
}

if [info exist Opt(ks-meta)] { append recipe_ks_meta " $Opt(ks-meta)" }

set ROLE_LIST {}
if [info exist Opt(topo)] {
	if ![regexp -nocase {single} $Opt(topo)] {
		lassign [regsub -all {[^0-9]+} $Opt(topo) " "] Opt(servers) Opt(clients)
		if {$Opt(servers) == ""} {set Opt(servers) 1}
		if {$Opt(clients) == ""} {set Opt(clients) 1}
	}
}
if {[info exist Opt(servers)] && $Opt(servers) != 0} { lappend ROLE_LIST {*}[lrepeat $Opt(servers) SERVERS] }
if {[info exist Opt(clients)] && $Opt(clients) != 0} { lappend ROLE_LIST {*}[lrepeat $Opt(clients) CLIENTS] }
if {[llength $ROLE_LIST] == 0} { set ROLE_LIST STANDALONE }

# handle network-qe private NIC machines
set netqe_nic_opts {driver model speed match unmatch num}
foreach i $netqe_nic_opts {
	if [info exist Opt(netqe-nic-$i)] {
		set NETQE_NIC_OPTS_DICT($i) [split $Opt(netqe-nic-$i) ", "]
	}
}

set M 0
foreach role $ROLE_LIST {
	# set netqe-nic machine list for different role
	if [info exist NETQE_NIC_OPTS_DICT] {
		# init each params for parse_netqe_nic_info.sh
		foreach i $netqe_nic_opts { set $i {} }

		# get netqe-nic-* options key-val for each role
		foreach key [array names NETQE_NIC_OPTS_DICT] {
			set $key [lindex $NETQE_NIC_OPTS_DICT($key) $M]
			eval set val $$key
			if {$val==""} {set $key [lindex $NETQE_NIC_OPTS_DICT($key) 0]}
		}

		# set default values for parse_netqe_nic_info.sh params
		if {$driver==""} {set driver any}
		if {$model==""} {set model any}
		if {$speed==""} {set speed any}
		if {$match==""} {set match any}
		if {$unmatch ==""} {set unmatch ''}
		if {$num==""} {set num 1}

		# get ormachine_list for each role
		set NETQE_NIC_MACHINE($role) [exec parse_netqe_nic_info.sh -d "$driver" -m "$model" -s "$speed" -p "$match" -v "$unmatch" -c "$num"]
		incr M 1
	} {
		set NETQE_NIC_MACHINE($role) {}
	}
}

set jobCtl {!}
set wbCtl {-}
set notifyCtl {!}
if [info exist Opt(recipe)] {
	set jobCtl {c}
	set wbCtl {_}
	set notifyCtl {C}
}

set jobOwner {}
if [info exist Opt(job-owner)] {
	set jobOwner $Opt(job-owner)
}

set taskN 1
if [info exist Opt(taskn)] {
	set taskN $Opt(taskn)
}

set retentionTag "60days"
set productkv {}
if [info exist Opt(retention-tag)] {
	set retentionTag $Opt(retention-tag)
	if [info exist Opt(product)] {
		set productkv product=$Opt(product)
	}
}

proc makeTag {taginfo} {
	if {[string range $taginfo 0 2] == "kv-"} {
		set t [string range $taginfo 3 end]
		lassign [regsub (.*?)(=|!=|>|>=|<|<=|~)(.*$) $t {key=\1 op=\2 {value=\3}}] k op v
		if {$op == "op=~"} {set op "op=like"}
		doTag key_value $k $op $v -
	} else {
		set _tag [split $taginfo ,]
		set _tag0 [lindex $_tag 0]
		set _attrs [list]
		if {[llength $_tag] > 1} {lappend _attrs {*}[lrange $_tag 1 end]}

		lassign [regsub (.*?)(:|=|!=|>|>=|<|<=|~)(.*$) $_tag0 {\1 op=\2 {value=\3}}] t op v
		if {$op == "op=~"} {set op "op=like"}
		if {$op == "op=:"} {
			set _attrs [linsert $_attrs 0 [string range $v 6 end]]
		} else {
			set _attrs [linsert $_attrs 0 $op $v]
		}
		doTag $t {*}$_attrs -
	}
}

# start generate xml
job retention_tag=$retentionTag $productkv user=$jobOwner $jobCtl {
	if ![info exist Opt(wb)] {
		set Time [clock format [clock seconds] -format %Y-%m-%d~%H:%M]
		set Opt(wb) "\[$Time\] $DISTRO_L \\[llength $TaskList]/[file tail [lindex [lindex $TaskList 0] 0]],... arch=$ARCH_L "
	}
	whiteboard $wbCtl "<!\[CDATA\[$Opt(wb)]]>"
	notify $notifyCtl {
		if [info exist Opt(cc)] {
			foreach e $Opt(cc) {
				foreach m [split $e ", "] { cc - $m }
			}
		}
	}
	recipeSet priority=Normal ! {
		set SystemType "Machine"
		set VARIANT {}
		set DISTRO {}
		set FAMILY {}
		set TAG {STABLE}
		set R {0};	# Role index
		foreach role ${ROLE_LIST} {
			if [llength $DISTRO_L] {
				set DISTRO [lindex $DISTRO_L 0]
				set DISTRO_L [lrange $DISTRO_L 1 end]
			}
			if [llength $FAMILY_L] {
				set FAMILY [lindex $FAMILY_L 0]
				set FAMILY_L [lrange $FAMILY_L 1 end]
			}
			if [llength $TAG_L] {
				set TAG [lindex $TAG_L 0]
				set TAG_L [lrange $TAG_L 1 end]
			}
			if [llength $ARCH_L] {
				set ARCH [lindex $ARCH_L 0]
				set ARCH_L [lrange $ARCH_L 1 end]
			}

			lassign [split $ARCH .] ARCH Vendor
			if ![string length $ARCH] {set ARCH auto}
			if [string equal $ARCH "auto"] {set ARCH x86_64}

			set MACHINE {}
			if [llength $MACHINE_L] {
				set MACHINE [lindex $MACHINE_L 0]
				set MACHINE_L [lrange $MACHINE_L 1 end]
				#if {[string length $MACHINE] > 0} { set ARCH {} }
			}

			set KOPTS ""
			set KOPTS_POST ""
			if [info exist Opt(k-opts)] {
				set KOPTS $Opt(k-opts)
				if {[llength $Opt(k-opts)] > 1} { set KOPTS [lindex $Opt(k-opts) $R]}
			}
			if [info exist Opt(k-opts-post)] {
				set KOPTS_POST $Opt(k-opts-post)
				if {[llength $Opt(k-opts-post)] > 1} { set KOPTS_POST [lindex $Opt(k-opts-post) $R]}
			}
			set recipe_wb $Opt(wb)
			if [info exist Opt(rwb)] { set recipe_wb "$Opt(rwb)" }

			lappend Opt(dr)
			lappend Opt(hr)
			set drs $Opt(dr)
			set hrs $Opt(hr)

			if {[info exist Opt(p9)] != 1} {
				lappend hrs not:kv-CPUMODEL~POWER9%
			}
			recipe kernel_options=$KOPTS kernel_options_post=$KOPTS_POST whiteboard=$recipe_wb ks_meta=$recipe_ks_meta ! {
				set pick "false"
				if [info exist Opt(random)] {set pick "true"}
				doTag autopick random=$pick -

				if [info exist Opt(packages)] {
					doTag packages ! {
						foreach pkg [split $Opt(packages) ",/"] {
							doTag package name=$pkg -
						}
					}
				}

				distroRequires ! {
					and ! {
						if [llength $VARIANT_L] {
							set VARIANT [lindex $VARIANT_L 0]
							set VARIANT_L [lrange $VARIANT_L 1 end]
						}
						lappend drs distro_variant=$VARIANT
						lappend drs distro_method=${METHOD}

						if [info exist Opt(DR)] {
							set DRs [lindex $Opt(DR) $R]
							if {$DRs != ""} {lappend drs {*}[split $DRs " "]}
						}

						if {$DISTRO ni {"" "family"}} {
							if ![regexp -- {^RedHatEnterpriseLinux} $DISTRO] {
								if {[lsearch -glob $drs distro_name=*] == -1} {lappend drs distro_name=${DISTRO}}
							} else {
								if {[lsearch -glob $drs distro_family=*] == -1} {lappend drs distro_family=${FAMILY}}
								if {[lsearch -glob $drs distro_tag=*] == -1} {lappend drs distro_tag=${TAG}}
							}
						} else {
							if {[lsearch -glob $drs distro_family=*] == -1} {lappend drs distro_family=${FAMILY}}
							if {[lsearch -glob $drs distro_tag=*] == -1} {lappend drs distro_tag=${TAG}}
						}

						if {[lsearch -glob $drs distro_arch=*] == -1} {
							set distro_arch $ARCH
							if {[string length $MACHINE] > 0} {
								set hostinfo [exec bash -c "curl -L -k -s $bkrurl/systems/$MACHINE"]
								set distro_arch [lindex [dict get [::json::json2dict $hostinfo] arches] end]
							}
							if [info exist Opt(HR)] {
								set HRs [lindex $Opt(HR) $R]
								set archindex [lsearch -glob $HRs arch=*]
								if {$archindex != -1} {
									set distro_arch [string range [lindex $HRs $archindex] 5 end]
								}
							}
							lappend drs distro_arch=${distro_arch}
						}

						foreach taginfo [lsearch -regexp -inline -all -not $drs {^(top|or|not)[-:]}] {
							makeTag $taginfo
						}
					}

					foreach taginfo [lsearch -glob -inline -all $drs {top[-:]*}] {
						set taginfo [string range $taginfo 4 end]
						makeTag $taginfo
					}

					or ! {
						foreach taginfo [lsearch -glob -inline -all $drs {or[-:]*}] {
							set taginfo [string range $taginfo 3 end]
							makeTag $taginfo
						}
					}
					not ! {
						foreach taginfo [lsearch -glob -inline -all $drs {not[-:]*}] {
							set taginfo [string range $taginfo 4 end]
							makeTag $taginfo
						}
					}
				}
				hostRequires ! {
					and ! {
						if {[string length $MACHINE] > 0} {
							hostname op=like value=$MACHINE -
						} else {
							if [info exist Opt(sr)] {lappend hrs {*}[lmap e $Opt(sr) {format "system.%s" $e}]}
							if {$Vendor != ""} {lappend Opt(kv) CPUVENDOR=$Vendor}
							if [info exist Opt(kv)] {lappend hrs {*}[lmap e $Opt(kv) {
								lassign [regsub (.*?)(=|!=|>|>=|<|<=|~)(.*$) $e {key=\1 op=\2 {value=\3}}] k op v
								if {$op == "op=~"} {set op "op=like"}
								format "key_value:%s,%s,%s" $k $op $v
							}]}

							if [info exist Opt(HR)] {
								set HRs [lindex $Opt(HR) $R]
								if {$HRs != ""} {lappend hrs {*}[split $HRs " "]}
							}
							if {[lsearch -glob $hrs arch=*] == -1} {
								lappend hrs arch=$ARCH
							}

							foreach taginfo [lsearch -regexp -inline -all -not $hrs {^(top|or|not)[-:]}] {
								makeTag $taginfo
							}

							if [info exist Opt(hrxml)] {
								puts "\n$Opt(hrxml)"
							}
						}
					}

					if [llength $SystemTypeList] {
						set SystemType [lindex $SystemTypeList 0]
						set SystemTypeList [lrange $SystemTypeList 1 end]
					}
					system_type op== value=${SystemType} -

					foreach taginfo [lsearch -glob -inline -all $hrs {top[-:]*}] {
						set taginfo [string range $taginfo 4 end]
						makeTag $taginfo
					}

					or ! {
						foreach m "$ORMACHINE_L $NETQE_NIC_MACHINE($role)" {
							hostname op=like value=$m -
						}
						foreach taginfo [lsearch -glob -inline -all $hrs {or[-:]*}] {
							set taginfo [string range $taginfo 3 end]
							makeTag $taginfo
						}
					}
					not ! {
						foreach taginfo [lsearch -glob -inline -all $hrs {not[-:]*}] {
							set taginfo [string range $taginfo 4 end]
							makeTag $taginfo
						}
					}
				}
				repos ! {
					if [info exist Opt(repo)] {
						set i 0
						foreach url $Opt(repo) {
							repo name=myrepo_[incr i] url=$url -
						}
					}
				}
				partitions ! {
					if [info exist Opt(part)] {
						foreach part $Opt(part) {
							partition {*}$part -
						}
					}
				}

				ks_appends ! {
					if [info exist Opt(repo-post)] {
						ks_append ! {
							puts {<![CDATA[}
							puts {%post}
							set i 0
							foreach url $Opt(repo-post) {
								puts "cat <<-EOF >/etc/yum.repos.d/beaker-kernel$i.repo
								\[beaker-kernel$i\]
								name=beaker-kernel$i
								baseurl=$url
								enabled=1
								gpgcheck=0
								skip_if_unavailable=1
								EOF"
								incr i
							}
							puts {%end}
							puts -nonewline {]]>}
						}
					}

					set default-ksf "/etc/beaker/default-ks.cfg"
					if {![info exist Opt(ksf)] || $Opt(ksf) == ""} {
						set Opt(ksf) ${default-ksf}
					}
					if [info exist Opt(ks-append)] {
						foreach ks $Opt(ks-append) {
							ks_append ! {
								puts "<!\[CDATA\[\n$ks\]\]>"
							}
						}
					}
					if [file isfile $Opt(ksf)] {
						if {![catch {set fp [open $Opt(ksf)]} err]} {
							ks_append ! {
								puts "<!\[CDATA\[\n[read $fp]\]\]>"
							}
							close $fp
						}
					}
				}

				if {[info exist Opt(reserve-if-fail)] || [info exist Opt(ignore-panic)]} {
					watchdog panic=ignore -
				}

				# tasks nodes
				set insertTasks [list]

				# distro install
				lappend insertTasks "/distribution/install DISTRO_BUILD=$DISTRO"

				# command before kernelinstall
				if [info exist Opt(cmdb)] {
					foreach cmd $Opt(cmdb) {
						lappend insertTasks "/distribution/command {CMDS_TO_RUN=$cmd}"
					}
				}

				# kernelinstall
				if ![info exist NVR] { set NVR {} }
				if [llength $NVR_L] {
					set NVR [lindex $NVR_L 0]
					if {$NVR in {"" "{}"}} {
						set NVR [getDefaultNVR $DISTRO]
					}
					set NVR_L [lrange $NVR_L 1 end]
				}

				set UP ""
				if {$NVR ni {"" "{}"}} {
					if ![regexp {^upstream.*} $NVR] {
						set KN [regsub {((^[a-z][a-z-]*)-)?.*} $NVR {\2}]
						if {$KN == ""} {set KN kernel}
						set KVR [regsub {^[a-z-]*} $NVR {}]
						set params [list]
						lappend params KERNELARGNAME=$KN KERNELARGVERSION=$KVR KERNELARGVARIANT=$KVARIANT NoDeps=--nodeps
						lappend insertTasks "/distribution/kernelinstall $params"
					} else {
						set UP [regsub {^upstream} $NVR {}]
						set UP [regsub {^[:=]} $UP {}]

						set upstreamUrl git://git.app.eng.bos.redhat.com/linux.git
						set upstreamBranch master
						set upstreamTag HEAD
						if {$UP != ""} {
							lassign [split $UP] url_branch tag
							lassign [split $url_branch "#"] url branch
							if {[string trim $url] != ""} {set upstreamUrl $url}
							if {[string trim $branch] != ""} {set upstreamBranch $branch}
							if {[string trim $tag] != ""} {set upstreamTag $tag}
						}
						set UP 1
					}
				} elseif {[info exist Opt(dbgk)] && ![info exist Opt(upstream)]} {
					set NVR [getDefaultNVR $DISTRO]
					set KN [regsub {((^[a-z][a-z-]*)-)?.*} $NVR {\2}]
					if {$KN == ""} {set KN kernel}
					set KVR [regsub {^[a-z-]*} $NVR {}]
					set params [list]
					lappend params KERNELARGNAME=$KN KERNELARGVERSION=$KVR KERNELARGVARIANT=$KVARIANT NoDeps=--nodeps
					lappend insertTasks "/distribution/kernelinstall $params"
				}
				if {[info exist Opt(upstream)] || $UP != ""} {
					set params [list]
					lappend params KERNEL_GIT_REPO=$upstreamUrl KERNEL_GIT_BRANCH=$upstreamBranch KERNEL_GIT_COMMIT=$upstreamTag ABORT_IF_FAIL=1
					if [info exist Opt(upstream-use-clone)] {
						lappend params USE_GIT_CLONE=1 GIT_CLONE_OPTION=$Opt(upstream-use-clone)
					}
					if [info exist Opt(upstream-kernel-kasan)] {
						lappend params ENABLE_KASAN=1
					}
					if [info exist Opt(upstream-patch)] {
						lappend params PATCH_URLS=$Opt(upstream-patch)
					}
					lappend insertTasks "/kernel/distribution/upstream-kernel/install $params"

				}
				if [info exist Opt(Scratch)] {
					foreach bid $Opt(Scratch) {
						lappend insertTasks "/distribution/scratch-build-install BUILDID=$bid"
					}
				}
				if [info exist Opt(scratch)] {
					set scratch [lindex $Opt(scratch) 0]
					set Opt(scratch) [lrange $Opt(scratch) 1 end]
					foreach bid $scratch {
						lappend insertTasks "/distribution/scratch-build-install BUILDID=$bid"
					}
				}

				# --kdump task
				if {[info exist Opt(kdump)] && ![info exist Opt(nokdump)]} {
					set kdumpAddr $Opt(kdump)
					if {$kdumpAddr == "" && [info exist KDUMP_ADDR]} {
						set kdumpAddr $KDUMP_ADDR
					}
					lassign [regsub (.*?):(/.*$) $kdumpAddr {\1 {\2}}] NFSSERVER VMCOREPATH
					if { [ string length $NFSSERVER ] == 0 } {
						set NFSSERVER "ibm-x3250m4-06.rhts.eng.pek2.redhat.com"
					}
					if { [ string length $VMCOREPATH ] == 0 } {
						set VMCOREPATH "/workspace/vmcore"
					}
					lappend insertTasks "/kernel/kdump/setup-nfsdump NFSSERVER=${NFSSERVER} VMCOREPATH=${VMCOREPATH} NO_COMPRESS=1"
				}

				# enable fips
				if [info exist Opt(fips)] {
					set FIPS {1}
					if [llength $FIPS_L] {
						set FIPS [lindex $FIPS_L 0]
						set FIPS_L [lrange $FIPS_L 1 end]
					}
					if {$FIPS ni {"0"}} {
						lappend insertTasks "/distribution/fips/setup-fips-enabled"
					} else {
						lappend insertTasks "/distribution/fips/setup-fips-disabled"
					}
				}

				# enable abrt
				if [info exist Opt(abrt)] {
					lappend insertTasks "/distribution/crashes/enable-abrt"
				}

				# command after kernelinstall
				if [info exist Opt(cmd)] {
					foreach cmd $Opt(cmd) {
						lappend insertTasks "/distribution/command {CMDS_TO_RUN=$cmd}"
					}
				}
				# pkg install
				if [info exist Opt(install)] {
					foreach pkg $Opt(install) {
						lappend insertTasks "/distribution/pkginstall PKGARGNAME=$pkg"
					}
				}
				# --kcov --gcov task
				if [info exist Opt(kcov)] {
					if {$Opt(kcov) != ""} {
						set kdir $Opt(kcov)
					} else {
						set kdir "fs, net, drivers/net"
					}

					lappend insertTasks "/kernel/kcov/prepare MODE=KA {KDIR=$kdir}"
					lappend insertTasks "/kernel/kcov/start"
				}
				if [info exist Opt(gcov)] {
					lappend insertTasks "/kernel/kcov/gcov-userspace-start PACKAGE_NAME=$Opt(gcov)"
				}

				# --leap-second
				if [info exist Opt(leap-second)] {
					lappend insertTasks "/kernel/general/time/leap-second/ins_leap_sec"
				}

				if [info exist Opt(insert-task)] {
					lappend insertTasks $Opt(insert-task)
				}

				foreach taskb $insertTasks {
					set taskname [lindex $taskb 0]
					set taskparams [lrange $taskb 1 end]
					task name=$taskname role=$role ! {
						params ! {
							foreach taskparam $taskparams {
								regexp {^([^=]+)=(.*)$} $taskparam _ _name _value
								param name=$_name value=$_value -
							}
						}
					}
				}

				# task list
				set llen [llength ${TaskList}]
				for {set i 0} {$i < $taskN} {incr i} {
					set iter 1
					foreach task ${TaskList} {
						set name [lindex $task 0]
						set arglist [lrange $task 1 end]
						task name=$name role=$role ! {
							if [info exist fetch_test_from_git] {
								set trim/ 1
								if [regexp {^/CoreOS/} $name] {
									set trim/ 2
								}
								if [info exist Opt(restraint-case-trim/)] {
									set trim/ $Opt(restraint-case-trim/)
								}
								doTag fetch url=[regsub {#+$} $restraint_git {}]#[regsub "^(/+\[^/]+){0,${trim/}}/?" $name {}] -
							}
							params ! {
								foreach arg $arglist {
									lassign [regsub {(^[^=]*)=(.*)} $arg {\1 {\2}}] pname pvalue
									# use "mh-" prefix to set different value for multihost
									if [regexp {^mh-} $pname] {
										set pname [string range $pname 3 end]
										set pvalue_list [split $pvalue ,]
										param name=$pname value=[lindex $pvalue_list $R] -
									} else {
										param name=$pname value=$pvalue -
									}
								}
							}
						}

						if {[info exist Opt(reboot)] && $iter < $llen} {
							task name=/distribution/utils/reboot role=$role -
						}
						incr iter
					}
				}

				set appendTasks [list]
				if [info exist Opt(append-task)] {
					lappend appendTasks $Opt(append-task)
				}

				# --kcov --gcov task
				if [info exist Opt(kcov)] {
					lappend appendTasks "/kernel/kcov/end KILLTIMEOVERRIDE=345600"
					lappend appendTasks "/kernel/kcov/finalize"
					#lappend appendTasks "/kernel/kcov/finalize NFS_SHARE=$NFS_SHARE TASKNAME=$TASKNAME"
				}
				if [info exist Opt(gcov)] {
					lappend appendTasks "/kernel/kcov/gcov-userspace-end PACKAGE_NAME=$Opt(gcov)"
				}

				# command in the end of the recipe
				if [info exist Opt(cmd-end)] {
					foreach cmd $Opt(cmd-end) {
						lappend appendTasks "/distribution/command {CMDS_TO_RUN=$cmd}"
					}
				}

				# --reserve-if-fail
				if [info exist Opt(reserve-if-fail)] {
					set ReserveTime {356400}
					if {[string length $Opt(reserve-if-fail)] > 0} {
						set ReserveTime $Opt(reserve-if-fail)
					}
					lappend appendTasks "/distribution/reservesys ESERVETIME=${ReserveTime} RESERVE_IF_FAIL=1"
				}
				# --reserve
				if [info exist Opt(reserve)] {
					set ReserveTime {356400}
					if {[string length $Opt(reserve)] > 0} {
						set ReserveTime $Opt(reserve)
					}
					lappend appendTasks "/distribution/reservesys ESERVETIME=${ReserveTime}"
				}

				foreach taska $appendTasks {
					set taskname [lindex $taska 0]
					set taskparams [lrange $taska 1 end]
					task name=$taskname role=$role ! {
						params ! {
							foreach taskparam $taskparams {
								regexp {^([^=]+)=(.*)$} $taskparam _ _name _value
								param name=$_name value=$_value -
							}
						}
					}
				}

			}
			set R [ expr $R + 1 ]
		}
	}
}
puts ""
